package io.kiki.sba.registry.common.model;


import io.kiki.sba.registry.common.model.dataserver.DatumDigest;
import io.kiki.sba.registry.common.model.dataserver.DatumSummary;
import lombok.Getter;
import lombok.Setter;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

@Setter
@Getter
public final class PublisherDigestUtil {
    private PublisherDigestUtil() {
    }

    public static Map<String, DatumDigest> digest(Map<String, DatumSummary> dataInfoIdToDatumSummaryMap) {
        if (dataInfoIdToDatumSummaryMap.isEmpty()) {
            return Collections.emptyMap();
        }

        final Map<String, DatumDigest> map = new HashMap<>(dataInfoIdToDatumSummaryMap.size());
        for (Map.Entry<String, DatumSummary> entry : dataInfoIdToDatumSummaryMap.entrySet()) {
            final String dataInfoId = entry.getKey();
            map.put(dataInfoId, digest(entry.getValue()));
        }
        return map;
    }

    public static DatumDigest digest(DatumSummary datumSummary) {
        int publisherNum = datumSummary.size();
        long publisherIdSign = 0;
        long publisherVerSign = 0;
        long publisherTimestampSign = 0;

        if (datumSummary.isEmpty()) {
            return new DatumDigest(publisherNum, publisherIdSign, publisherVerSign, publisherTimestampSign, (short) 0, (short) 0);
        }

        final TreeMap<String, RegisterVersion> sorted = new TreeMap<>(datumSummary.getRegisterIdToRegisterVersionMap());
        long maxTimestamp = 0;
        long minTimestamp = Long.MAX_VALUE;
        for (Map.Entry<String, RegisterVersion> entry : sorted.entrySet()) {
            final String registerId = entry.getKey();
            final long digestRegisterId = digest(registerId);
            publisherIdSign = publisherIdSign * 31 + digestRegisterId;
            final RegisterVersion ver = entry.getValue();
            publisherVerSign = 31 * publisherVerSign + ver.getVersion();
            publisherTimestampSign = 31 * publisherTimestampSign + ver.getRegisterTimestamp();
            if (minTimestamp > ver.getRegisterTimestamp()) {
                minTimestamp = ver.getRegisterTimestamp();
            }
            if (maxTimestamp < ver.getRegisterTimestamp()) {
                maxTimestamp = ver.getRegisterTimestamp();
            }
        }
        // save 16bits
        final short max = (short) maxTimestamp;
        final short min = (short) minTimestamp;
        return new DatumDigest(publisherNum, publisherIdSign, publisherVerSign, publisherTimestampSign, max, min);
    }

    private static int digest(String str) {
        // use string.hashCode, it's the fastest. the calc result has cached.
        // but must pay attention to the compatibility of different jdk versions
        // after jdk1.2, the java doc promise:
        // The hash code for a String object is computed as
        // s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
        return str.hashCode();
    }
}
